#include "gifdecoder.h"
#include "gifapplicationextension.h"
#include "gifcommentextension.h"
#include "gifgraphiccontrolextension.h"
#include "gifplaintextextension.h"
#include "giftrailer.h"

namespace WingGif {

GifDecoder::GifDecoder(QObject *parent) : QObject(parent) {}

bool GifDecoder::decode(QIODevice *stream) {
  bool res;
  _header = new GifHeader(this);
  _header->read(stream, QList<GifExtension *>());

  if (_header->logicalScreenDescriptor()->hasGlobalColorTable())
    _globalColorTable = GifFrame::readColorTable(
        stream, _header->logicalScreenDescriptor()->globalColorTableSize());

  res = readFrames(stream);
  if (!res)
    return false;

  GifApplicationExtension *netscapeExtension = nullptr;
  for (auto item : _extensions) {
    auto p = qobject_cast<GifApplicationExtension *>(item);
    if (p) {
      if (p->isNetscapeExtension()) {
        netscapeExtension = p;
        break;
      }
    }
  }
  _repeatCount = netscapeExtension != nullptr
                     ? netscapeExtension->getRepeatCount()
                     : (ushort)1;
  return true;
}

GifHeader *GifDecoder::header() { return _header; }

QVector<GifColor> GifDecoder::globalColorTable() { return _globalColorTable; }

QList<GifFrame *> GifDecoder::frames() { return _frames; }

QList<GifExtension *> GifDecoder::extensions() { return _extensions; }

ushort GifDecoder::repeatCount() { return _repeatCount; }

bool GifDecoder::readFrames(QIODevice *stream) {
  QList<GifFrame *> frames;
  QList<GifExtension *> controlExtensions;
  QList<GifExtension *> specialExtensions;

  while (stream->pos() < stream->size()) {

    char blockId;
    stream->read(&blockId, 1);

    if (blockId < 0)
      return false;
    QList<GifExtension *> emptyParam;
    GifBlockKind blockKind;

    switch (blockId) {
    case BlokID::ExtensionIntroducer: {
      // Note: at this point, the Extension Introducer (0x21) has already been
      // read
      char label;

      stream->read(&label, 1);

      if (label < 0)
        return false;

      GifExtension *ext;
      switch (ExtensionLabel(label)) {
      case ExtensionLabel::GifGraphicControlExtension: {
        ext = new GifGraphicControlExtension(this);
        ext->read(stream, emptyParam);
        blockKind = ext->kind();
        break;
      }
      case ExtensionLabel::GifCommentExtension: {
        ext = new GifCommentExtension(this);
        ext->read(stream, emptyParam);
        blockKind = ext->kind();
        break;
      }
      case ExtensionLabel::GifPlainTextExtension: {
        ext = new GifPlainTextExtension(this);
        ext->read(stream, emptyParam);
        blockKind = ext->kind();
        break;
      }
      case ExtensionLabel::GifApplicationExtension: {
        ext = new GifApplicationExtension(this);
        ext->read(stream, emptyParam);
        blockKind = ext->kind();
        break;
      }
      default:
        return false;
      }

      switch (blockKind) {
      case GifBlockKind::Control:
        controlExtensions.append(ext);
        break;
      case GifBlockKind::SpecialPurpose:
        specialExtensions.append(ext);
        break;
      default:
        break;
      }
    } break;
    case BlokID::ImageSeparator: {
      auto frame = new GifFrame(this);
      frame->read(stream, controlExtensions);
      frames.append(frame);
      blockKind = frame->kind();
      break;
    }
    case BlokID::TrailerByte: {
      GifTrailer g;
      g.read(nullptr, emptyParam);
      blockKind = g.kind();
      break;
    }
    default:
      return false;
    }
    if (blockKind == GifBlockKind::GraphicRendering)
      controlExtensions.clear();
  }

  _frames.swap(frames);
  _extensions.swap(specialExtensions);
  return true;
}

} // namespace WingGif
